﻿using System;
using System.Collections.Generic;
using System.Linq;
using System.Text;
using System.Windows;
using System.Windows.Controls;
using System.Windows.Data;
using System.Windows.Documents;
using System.Windows.Input;
using System.Windows.Media;
using System.Windows.Media.Imaging;
using System.Windows.Navigation;
using System.Windows.Shapes;
using System.Windows.Markup;
using System.Windows.Controls.Primitives;
using System.Collections.ObjectModel;
using System.Collections.Specialized;
using System.IO;
using UmlControlLibrary.AStar;

//TODO:
// génération relations : distance par rapport à la classe
//                        déselectionner la relation avant le routing
//                        ajouter les relations à la map avant routing
// layout graphviz
//
// import/export xmi
// evenements
// analyse saisie regexp
// interface
// enum
// stereotypes
// customization couleurs

namespace UmlControlLibrary
{
    /// <summary>
    /// Interaction logic for UmlDiagram.xaml
    /// </summary>
    public partial class UmlDiagram : SelectableItemControl, IDraggable
    {
        private static ResourceDictionary theme = null;
         private const string ThemeUri = @"PresentationFramework.Aero;V3.0.0.0;31bf3856ad364e35;component\themes/aero.normalcolor.xaml";       
        // private const string ThemeUri = @"PresentationFramework.Luna;V3.0.0.0;31bf3856ad364e35;component\themes/luna.normalcolor.xaml";
        // private const string ThemeUri = @"PresentationFramework.Royale;V3.0.0.0;31bf3856ad364e35;component\themes/royale.normalcolor.xaml";

        public ObservableCollection<SelectableItemControl> Childs = new ObservableCollection<SelectableItemControl>();

        public Thumb AddThumb(double x, double y)
        {
            Thumb t = new Thumb();
            t.Height = 8;
            t.Width = 8;
            Canvas.SetLeft(t, x - 4);
            Canvas.SetTop(t, y - 4);
            return t;
        }
        public UmlDiagram():base()
        {
            InitializeComponent();
            if (theme == null)
            {
                Uri uri = new Uri(ThemeUri, UriKind.Relative);
                theme = Application.LoadComponent(uri) as ResourceDictionary;
            }

            this.Resources.MergedDictionaries.Add(theme);
            AddDragZone(rootCanvas);

            UmlElementList.instance.Elements.CollectionChanged += new System.Collections.Specialized.NotifyCollectionChangedEventHandler(Classes_CollectionChanged);

            this.ContextMenu = new UmlDiagramContextMenu();
            this.ContextMenuOpening += new ContextMenuEventHandler(UmlDiagram_ContextMenuOpening);

            UmlClass _c = new UmlClass("Classe A");
            _c.PropertyList.Add(new UmlProperty("property A1"));
            _c.PropertyList.Add(new UmlProperty("property A2"));
            _c.PropertyList.Add(new UmlProperty("property A3"));
            _c.PropertyList.Add(new UmlProperty("property A4"));
            _c.PropertyList.Add(new UmlProperty("property A5"));
            _c.PropertyList.Add(new UmlProperty("property A6"));
            _c.MethodList.Add(new UmlProperty("method A1"));
            _c.MethodList.Add(new UmlProperty("method A2"));
            _c.MethodList.Add(new UmlProperty("method A3"));
            _c.MethodList.Add(new UmlProperty("method A4"));

            _c.DiagramElementPropertyChanged += new DiagramElementPropertyChangedDelegate(_c_DiagramElementPropertyChanged);


            UmlElementList.instance.AddElement(_c);

            UmlInterface _i;
            _i = new UmlInterface("Interface B");
            _i.MethodList.Add(new UmlProperty("method B1"));
            _i.MethodList.Add(new UmlProperty("method B2"));
            _i.MethodList.Add(new UmlProperty("method B3"));
            _i.MethodList.Add(new UmlProperty("method B4"));
            _i.Stereotype = "interface";

            UmlElementList.instance.AddElement(_i);
        }

        void UmlDiagram_ContextMenuOpening(object sender, ContextMenuEventArgs e)
        {
            (this.ContextMenu as UmlDiagramContextMenu).SetMenuContexts(e, this, this);
        }

        void _c_DiagramElementPropertyChanged(DiagramElementPropertyChangedEventArgs args)
        {
        }

        void Classes_CollectionChanged(object sender, NotifyCollectionChangedEventArgs e)
        {
            switch (e.Action)
            {
                case NotifyCollectionChangedAction.Add:
                    foreach(DiagramElement c in e.NewItems)
                        AddNewDiagramElement(c);
                    break;
                case NotifyCollectionChangedAction.Remove:
                    break;
                case NotifyCollectionChangedAction.Replace:
                    break;
                case NotifyCollectionChangedAction.Reset:
                    break;
            }
        }

        public void AddNewDiagramElement(DiagramElement _element)
        {
            AddNewDiagramElement(_element, (Childs.Count + 1) * 200, 200);
        }
        public void AddNewDiagramElement(DiagramElement _element, double x, double y)
        {
            UmlControl c = new UmlControl();
            c.DataContext = _element;
            c.Template = (ControlTemplate)this.Resources["DraggableAndResizableDesignerItem"];
            c.ApplyTemplate();

            Canvas.SetLeft(c, SelectableItemControl.AlignToGrid(x));
            Canvas.SetTop(c, SelectableItemControl.AlignToGrid(y));

            this.AddChild(c);

            ControlTemplate ct = (ControlTemplate)c.FindResource("DraggableAndResizableDesignerItem");
            Control Draggable = (Control)ct.FindName("DraggableClassView",c);

            //            ControlTemplate subCt = (ControlTemplate)this.Resources["Drag" + _class.ItemType + "Template"];
            ControlTemplate subCt = _element.GetElementTemplate();
            if (subCt != null)
            {
                Draggable.Template = subCt;
                Draggable.ApplyTemplate();
            }
        }

        #region Canvas Size Calculation

        private List<SelectableItemControl> XPosList = new List<SelectableItemControl>();
        private List<SelectableItemControl> YPosList = new List<SelectableItemControl>();

        public void ResizeRootCanvasWidth()
        {
            XPosList.Sort(delegate(SelectableItemControl c1, SelectableItemControl c2)
            { return c1.Right.CompareTo(c2.Right); }
              );
            double max = this.ActualWidth;
            if (XPosList.Count > 0)
            {
                SelectableItemControl last = XPosList.Last<SelectableItemControl>();
                max = Math.Max(last.Right, max);
            }
            rootCanvas.Width = max;
        }

        public void ResizeRootCanvasHeight()
        {
            YPosList.Sort(delegate(SelectableItemControl c1, SelectableItemControl c2)
            { return c1.Bottom.CompareTo(c2.Bottom); }
              );
            double max = this.ActualHeight;
            if (YPosList.Count > 0)
            {
                SelectableItemControl last = YPosList.Last<SelectableItemControl>();
                max = Math.Max(last.Bottom, max);
            }
            rootCanvas.Height = max;
        }

        public void ResizeRootCanvas()
        {
            ResizeRootCanvasWidth();
            ResizeRootCanvasHeight();
        }

        #endregion

        #region childs changes handling

        internal void ChildUpdated(SelectableItemControl child)
        {
            SelectableItemControl last;
            if (child.Right >= rootCanvas.Width + 16)
            {
                rootCanvas.Width = child.Right + 16;
                last = XPosList.Last<SelectableItemControl>();
                if (last != child)
                {
                    XPosList.Remove(child);
                    XPosList.Add(child);
                }
            }
            else
                ResizeRootCanvasWidth();
            
            if (child.Bottom >= rootCanvas.Height + 16)
            {
                rootCanvas.Height = child.Bottom + 16;
                last = YPosList.Last<SelectableItemControl>();
                if (last != child)
                {
                    YPosList.Remove(child);
                    YPosList.Add(child);
                }
            }
            else
                 ResizeRootCanvasHeight();
        }


        public override void ChildMoved(SelectableItemControl child)
        {
            base.ChildMoved(child);
            ChildUpdated(child);
        }

        public override void ChildResized(SelectableItemControl child)
        {
            base.ChildResized(child);
            ChildUpdated(child);
        }

/*        public override IAddChild ChildContainer{
            get { return rootCanvas; }
        }*/

        public override void AddChild(object value)
        {
            if (value is SelectableItemControl)
            {
                SelectableItemControl control = (SelectableItemControl)value;
                control.OwnerElement = this;
                rootCanvas.Children.Add(control);
                control.CalculateBounds();
                Childs.Add(control);
                XPosList.Add(control);
                YPosList.Add(control);
                ChildUpdated(control);
            }
            else if (value is UIElement)
            {
                rootCanvas.Children.Add((UIElement)value);
            }
            else
                base.AddChild(value);
        }

        public override void RemoveChild(object child)
        {
            if (child is SelectableItemControl)
            {
                SelectableItemControl c = (SelectableItemControl)child;
                if (Childs.Contains(c))
                    Childs.Remove(c);
            }
            if (child is UIElement)
            {
                UIElement e = (UIElement)child;
                if (rootCanvas.Children.Contains(e))
                    rootCanvas.Children.Remove(e);
            }
        }

        #endregion

        #region Events

        private void Canvas_SizeChanged(object sender, SizeChangedEventArgs e)
        {
            ResizeRootCanvas();
            double h = ActualHeight;
            double w = ActualWidth;
            ScrollViewer1.Height = h;
            ScrollViewer1.Width = w;
        }


        #endregion Events

        #region IDraggable Members

        public void Drag(double deltaX, double deltaY)
        {
            ScrollViewer1.ScrollToHorizontalOffset(ScrollViewer1.HorizontalOffset - deltaX);
            ScrollViewer1.ScrollToVerticalOffset(ScrollViewer1.VerticalOffset - deltaY);
            LastDragPos = new Point(LastDragPos.X - deltaX, LastDragPos.Y- deltaY);
        }

        public override Point GetMousePos(MouseEventArgs e)
        {
            return e.GetPosition(rootCanvas);
        }

        #endregion

        private void button1_Click(object sender, RoutedEventArgs e)
        {
            UmlRelation r = new UmlRelation(Childs[0] as UmlControl, Childs[1] as UmlControl, UmlRelationType.Aggregate);
            r.End1.Label = "1";
            r.End2.Label = "1..*";
            r.Label = "TestRelation";

            DraggableLabel lbl = new DraggableLabel();
            lbl.Text = "test de label";

            Canvas.SetLeft(lbl, 500);
            Canvas.SetTop(lbl, 500);
            lbl.OwnerElement = this;
            rootCanvas.Children.Add(lbl);

        }       

        private void button3_Click(object sender, RoutedEventArgs e)
        {
            AStarRouteRelation((Childs[0] as UmlControl).Relations[0]);

            //CurrentAStarMap = GetMap();

            //AStar.AStar aStar = new AStar.AStar(CurrentAStarMap);
            //List<AStarNode> Path = aStar.Run();

            //foreach (AStarNode node in Path)
            //    CurrentAStarMap[node.X, node.Y] = AStarNode.NodeStatus.Edge;

            //Path = AStar.AStar.TrimPath(Path);


            //UmlRelation r = new UmlRelation(Childs[0] as UmlControl, Childs[1] as UmlControl, UmlRelationType.Aggregate);
            //r.End1.Label = "1";
            //r.End2.Label = "1..*";
            //r.Label = "TestRelation";

            //PolyLineSegment pls = (r.LineFigure.Segments[0] as PolyLineSegment);
            //for (int i = pls.Points.Count - 2; i >= 1; i--)
            //    pls.Points.RemoveAt(i);

            //foreach (AStarNode node in Path)
            //    pls.Points.Insert(pls.Points.Count - 1, new Point(node.X * AStarNode.NodeSize, node.Y * AStarNode.NodeSize));
            //r.RecalcRelationLink();

            //StreamWriter sw = new StreamWriter("c:\\map.txt");
            //for (int y = 0; y < CurrentAStarMap.GetLength(1); y++)
            //{
            //    for (int x = 0; x < CurrentAStarMap.GetLength(0); x++)
            //    {
            //        sw.Write((byte)CurrentAStarMap[x, y]);
            //    }
            //    sw.WriteLine();
            //}
            //sw.Flush();
            //sw.Close();
        }

        #region AStar

        AStarNode.NodeStatus[,] CurrentAStarMap = null;

        public AStarNode.NodeStatus[,] GetMap()
        {
            int mh = (int)Math.Round(rootCanvas.ActualHeight / AStarNode.NodeSize) + 5;
            int mw = (int)Math.Round(rootCanvas.ActualWidth / AStarNode.NodeSize) + 5;

            AStarNode.NodeStatus[,] map = new AStarNode.NodeStatus[mw, mh];

            foreach (SelectableItemControl control in Childs)
            {
                int l = (int)Math.Round(control.Left / AStarNode.NodeSize) - 1;
                int t = (int)Math.Round(control.Top / AStarNode.NodeSize) - 1;
                int r = (int)Math.Round(control.Right / AStarNode.NodeSize) + 1;
                int b = (int)Math.Round(control.Bottom / AStarNode.NodeSize) + 1;

                for (int y = t; y <= b; y++)
                    for (int x = l; x <= r; x++)
                        map[x, y] = AStarNode.NodeStatus.Block;
            }           
            return map;
        }

        public void AStarRouteRelation(UmlRelation r)
        {
            if (CurrentAStarMap == null)
                CurrentAStarMap = GetMap();
            PolyLineSegment pls = r.LineFigure.Segments[0] as PolyLineSegment;
            int sx = (int)Math.Round(pls.Points[0].X / AStarNode.NodeSize);
            int sy = (int)Math.Round(pls.Points[0].Y / AStarNode.NodeSize);
            int ex = (int)Math.Round(pls.Points[pls.Points.Count - 1].X / AStarNode.NodeSize);
            int ey = (int)Math.Round(pls.Points[pls.Points.Count - 1].Y / AStarNode.NodeSize);
            CurrentAStarMap[sx, sy] = AStarNode.NodeStatus.Start;
            CurrentAStarMap[ex, ey] = AStarNode.NodeStatus.Goal;

            AStar.AStar aStar = new AStar.AStar(CurrentAStarMap);
            List<AStarNode> Path = aStar.Run();

            CurrentAStarMap[sx, sy] = AStarNode.NodeStatus.Free;
            CurrentAStarMap[ex, ey] = AStarNode.NodeStatus.Free;
            
            /*    foreach (AStarNode node in Path)
                    CurrentAStarMap[node.X, node.Y] = AStarNode.NodeStatus.Edge;*/

            Path = AStar.AStar.TrimPath(Path);
           
            for (int i = pls.Points.Count - 2; i >= 1; i--)
                pls.Points.RemoveAt(i);

            foreach (AStarNode node in Path)
                pls.Points.Insert(pls.Points.Count - 1, new Point(node.X * AStarNode.NodeSize, node.Y * AStarNode.NodeSize));
            r.RecalcRelationLink();
        }

        #endregion

        /*        private UmlControl AddClass(string className, double x, double y)
        {
            UmlDesignerElement control = new UmlDesignerElement();
            control.OwnerDiagram = this;
            control.Template = (ControlTemplate)this.Resources["DraggableAndResizableDesignerItem"];
            Canvas.SetLeft(control, x);
            Canvas.SetTop(control, y);
            control.umlClass.ClassName = className;
            control.umlClass.PropertyList = new List<UmlProperty>();
            control.umlClass.PropertyList.Add(new UmlProperty("property 1"));
            control.umlClass.PropertyList.Add(new UmlProperty("property 2"));
            control.umlClass.PropertyList.Add(new UmlProperty("property 3"));
            control.umlClass.PropertyList.Add(new UmlProperty("property 4"));
            control.umlClass.PropertyList.Add(new UmlProperty("property 5"));
            control.umlClass.MethodList = new List<UmlProperty>();
            control.umlClass.MethodList.Add(new UmlProperty("+method 1"));
            control.umlClass.MethodList.Add(new UmlProperty("+method 2"));
            control.umlClass.MethodList.Add(new UmlProperty("+method 3"));
            control.umlClass.MethodList.Add(new UmlProperty("+method 4"));
            control.umlClass.MethodList.Add(new UmlProperty("+method 5"));
            control.DataContext = control.umlClass;
            //control.ownerDiagram = this;
            rootCanvas.Children.Add(control);
            XPosList.Add(control);
            YPosList.Add(control);

            control.ApplyTemplate();
            Control Draggable = control.Template.FindName("DraggableClassView", control) as Control;
            Draggable.ApplyTemplate();
            Control umlView = Draggable.Template.FindName("UmlClassView", Draggable) as Control;
            umlView.DataContext = control.DataContext;

            control.Moved(0, 0);
            //   control.DataContext = umlClass;
            //  view.umlClass = umlClass;

            UmlControls.Add(control);

            return control;
        }
*/

        /*
        
  



                private List<UmlControl> UmlControls = new List<UmlControl>();
                private List<UmlLine> UmlLines = new List<UmlLine>();

                public bool dragCanvas = false;

                private Path p;

                public void Link(UmlControl c1, UmlControl c2)
                {
                    c1.umlClass.Link(c2.umlClass);

                    UmlLine line = new UmlLine();
                    UmlLines.Add(line);
                    line.end1 = c1;
                    line.end2 = c2;
                    p = new Path();
                    p.Stroke = Brushes.Black;
                    p.DataContext = line;
                    p.SetBinding(Path.DataProperty, "PathGeometry");
                    line.Path = p;
                    rootCanvas.Children.Add(p);
                }

                public void UmlControlUpdated(UmlControl control)
                {
                    UmlControl last;
                    if (control.right >= rootCanvas.Width)
                    {
                        rootCanvas.Width = control.right;
                        last = XPosList.Last<UmlControl>();
                        if (last != control)
                        {
                            XPosList.Remove(control);
                            XPosList.Add(control);
                        }
                        //ScrollViewer1.ScrollToRightEnd();
                    }
                    else
                        ResizeRootCanvasWidth();

                    if (control.bottom >= rootCanvas.Height)
                    {
                        rootCanvas.Height = control.bottom;
                        last = YPosList.Last<UmlControl>();
                        if (last != control)
                        {
                            YPosList.Remove(control);
                            YPosList.Add(control);
                        }
                        //ScrollViewer1.ScrollToBottom();
                    }
                    else
                        ResizeRootCanvasHeight();
                }

                public void UmlControlMoved(UmlControl control)
                {
                    if (p != null)
                    {
                        BindingExpression e = p.GetBindingExpression(Path.DataProperty);
                        if (e != null)
                        {
                            e.UpdateTarget();
                        }
                    }
                }

                private UmlControl AddClass(string className, double x, double y)
                {
                    UmlControl control = new UmlControl();
                    control.OwnerDiagram = this;
                    control.Template = (ControlTemplate)this.Resources["DraggableAndResizableDesignerItem"];
                    Canvas.SetLeft(control, x);
                    Canvas.SetTop(control, y);
                    control.umlClass.ClassName = className;
                    control.umlClass.PropertyList = new List<UmlProperty>();
                    control.umlClass.PropertyList.Add(new UmlProperty("property 1"));
                    control.umlClass.PropertyList.Add(new UmlProperty("property 2"));
                    control.umlClass.PropertyList.Add(new UmlProperty("property 3"));
                    control.umlClass.PropertyList.Add(new UmlProperty("property 4"));
                    control.umlClass.PropertyList.Add(new UmlProperty("property 5"));
                    control.umlClass.MethodList = new List<UmlProperty>();
                    control.umlClass.MethodList.Add(new UmlProperty("+method 1"));
                    control.umlClass.MethodList.Add(new UmlProperty("+method 2"));
                    control.umlClass.MethodList.Add(new UmlProperty("+method 3"));
                    control.umlClass.MethodList.Add(new UmlProperty("+method 4"));
                    control.umlClass.MethodList.Add(new UmlProperty("+method 5"));
                    control.DataContext = control.umlClass;
                    //control.ownerDiagram = this;
                    rootCanvas.Children.Add(control);
                    XPosList.Add(control);
                    YPosList.Add(control);

                    control.ApplyTemplate();
                    Control Draggable = control.Template.FindName("DraggableClassView",control) as Control;
                    Draggable.ApplyTemplate();
                    Control umlView = Draggable.Template.FindName("UmlClassView", Draggable) as Control;
                    umlView.DataContext = control.DataContext;

                    control.Moved(0,0);
                 //   control.DataContext = umlClass;
                  //  view.umlClass = umlClass;

                    UmlControls.Add(control);

                    return control;
                }

                private void button1_Click(object sender, RoutedEventArgs e)
                {
                    this.AddClass("test 2", 100, 100);
                }

                public void EnableCanvasDrag()
                {
                    dragCanvas = true;
                    this.Cursor = Cursors.ScrollAll;
                }

                public void DisableCanvasDrag()
                {
                    dragCanvas = false;
                    this.Cursor = Cursors.Arrow;
                }

   /*             private void Canvas_MouseDown(object sender, MouseButtonEventArgs e)
                {
                    if (!DragThumb.canDrag)
                        EnableCanvasDrag();
                }

                private void rootCanvas_PreviewMouseLeftButtonUp(object sender, MouseButtonEventArgs e)
                {
                    DisableCanvasDrag();
                    if (!BSpline.CanDrag)
                       BSpline.Deselect();

                }*/

/*
                private void button2_Click(object sender, RoutedEventArgs e)
                {
                    GraphViz.DoLayout(UmlControls, UmlLines);
                }
        */

    }

    public class UmlLine
    {
        public UmlControl end1
        {
            get;
            set;
        }
        public UmlControl end2
        {
            get;
            set;
        }

        public double X1
        {
            get{return(Canvas.GetLeft(end1) + end1.ActualWidth / 2);}
        }
        public double Y1
        {
            get { return (Canvas.GetTop(end1) + end1.ActualHeight / 2); }
        }
        public double X2
        {
            get { return (Canvas.GetLeft(end2) + end2.ActualWidth / 2); }
        }
        public double Y2
        {
            get { return (Canvas.GetTop(end2) + end2.ActualHeight / 2); }
        }

        private PathGeometry pg = null;
        private Point p1 = new Point();
        private Point p2 = new Point();

        public System.Windows.Shapes.Path Path
        {
            get;
            set;
        }

        public PathGeometry PathGeometry
        {
            get
            {
                if (pg == null)
                {
                    pg = new PathGeometry();
                    PathFigure pf = new PathFigure();
                    p1.X = X1; p1.Y = Y1;
                    p2.X = X2; p2.Y = Y2;
                    pf.StartPoint = p1;
                    pf.Segments.Add(new LineSegment(p2, true));
                    pg.Figures.Add(pf);
                }
                else
                {
                    //PathFigure pf = pg.Figures[0];
                    //p1.X = X1; p1.Y = Y1;
                    //p2.X = X2; p2.Y = Y2;
                    //pf.StartPoint = p1;
                    //int i = pf.Segments.Count-1;
                    //LineSegment ls = (LineSegment)pf.Segments[i];
                    //ls.Point = p2;
                }
                return pg;
            }
            set
            {
                pg = value;
            }
        }

    }

    public class UmlControl: SelectableItemControl, IDraggable
    {
        public double bottom;
        public double right;

        public List<UmlRelation> Relations = new List<UmlRelation>();

       // public UmlDiagram OwnerDiagram;

        public bool Locked
        {
            get;
            set;
        }

        public UmlControl():base()
        {
            this.SizeChanged += new SizeChangedEventHandler(MyUmlControl_SizeChanged);
        }

        void  MyUmlControl_SizeChanged(object sender, SizeChangedEventArgs e)
        {
 	        //Moved(0,0);
        }

     /*   public void CalculateBounds()
        {
            bottom = Canvas.GetTop(this) + ActualHeight;
            right = Canvas.GetLeft(this) + ActualWidth;
        }*/

    /*    public void Moved(double deltaX, double deltaY)
        {
            CalculateBounds();
//            OwnerDiagram.UmlControlMoved(this);

            foreach (UmlLine line in StartLinks)
            { 
                PathFigure pf = line.PathGeometry.Figures[0];
                pf.StartPoint = new Point(pf.StartPoint.X + deltaX, pf.StartPoint.Y+deltaY);
                PolyBezierSegment bs = pf.Segments[0] as PolyBezierSegment;
                bs.Points[0] = new Point(bs.Points[0].X + deltaX, bs.Points[0].Y + deltaY);
                //               BindingOperations.GetBindingExpression(pf, PathFigure.StartPointProperty).UpdateTarget();
            }

            foreach (UmlLine line in EndLinks)
            {
                Point p1, p2;
                PathFigure pf = line.PathGeometry.Figures[0];
                PolyBezierSegment bs = pf.Segments[0] as PolyBezierSegment;

                if (StartLinks.Contains(line))
                {
                    Point ptemp = new Point();
                    Point p;
                    for (int i = 1; i < bs.Points.Count; i++)
                    {
                        p = bs.Points[i];
                        ptemp.X = p.X + deltaX;
                        ptemp.Y = p.Y + deltaY;
                        bs.Points[i] = ptemp;
                    }
                    p1 = bs.Points[bs.Points.Count - 2];
                    p2 = bs.Points[bs.Points.Count - 1];
                }
                else
                {
                    p1 = new Point(bs.Points[bs.Points.Count - 2].X + deltaX, bs.Points[bs.Points.Count - 2].Y + deltaY);
                    p2 = new Point(bs.Points[bs.Points.Count - 1].X + deltaX, bs.Points[bs.Points.Count - 1].Y + deltaY);
                    bs.Points[bs.Points.Count - 2] = p1;
                    bs.Points[bs.Points.Count - 1] = p2;
                }

                GraphViz.CalculateArrow(line.PathGeometry.Figures[1], p1, p2);
                //                BindingOperations.GetMultiBindingExpression(bs, PolyBezierSegment.PointsProperty).UpdateTarget();
            }

        }
        */
        public void MoveEnd()
        {
            //Moved(0,0);
 //           OwnerDiagram.UmlControlUpdated(this);
        }

    /*    public void AddStartLink(UmlLine line)
        {
            StartLinks.Add(line);
        }

        public void AddEndLink(UmlLine line)
        {
            EndLinks.Add(line);
        }*/

        private double getMagnitude(Point l1, Point l2)
        {
            double x, y;
            x = l2.X - l1.X; x *= x;
            y = l2.Y - l1.Y; y *= y;
            return x + y;
        }

        private double DistanceFromLine(Point p, Point l1, Point l2)
        {
            const double EPS = 0.000001;
            const double EPSEPS = EPS * EPS;
            double SqLineMag,u,ix,iy;
            double result;
            
            SqLineMag = getMagnitude(l1,l2);
            if (SqLineMag < EPSEPS)
                return -1;
 
            u = ((p.X-l1.X)*(l2.X-l1.X) + (p.Y-l1.Y)*(l2.Y-l1.Y)) / SqLineMag;           
            if ((u < EPS) || (u > 1))
            {
               ix = getMagnitude(p,l1);
               iy = getMagnitude(p,l2);
               result = Math.Min(ix,iy);   
            }
            else
            {
               ix = l1.X + u * (l2.X - l1.X);
               iy = l1.Y + u * (l2.Y - l1.Y);
               result = getMagnitude(p, new Point(ix, iy));
            }
            return Math.Sqrt(result);
        }

        public Point MagnetPoint(Point p)
        {
            LineHelper.Side side;
            return MagnetPoint(p, out side);
        }

        public Point MagnetPoint(Point p, out LineHelper.Side side)
        {
            double dt, db, dl, dr, d;
            double t = Canvas.GetTop(this),
                   b = Canvas.GetBottom(this),
                   l = Canvas.GetLeft(this),
                   r = Canvas.GetRight(this);

            if (double.IsNaN(b))
            {
                b = t + this.ActualHeight;
                r = l + this.ActualWidth;
            }

            double x = p.X, y = p.Y;
            side = LineHelper.Side.Top;

            if (x > r)
            {
                x = r;
                side = LineHelper.Side.Right;
            }
            if (x < l)
            {
                x = l;
                side = LineHelper.Side.Left;
            }
            if (y > b)
            {
                y = b;
                side = LineHelper.Side.Bottom;
            }
            if (y < t)
            {
                y = t;
                side = LineHelper.Side.Top;
            }

/*            Point tl = new Point(t,l),
                  tr = new Point(t,r),
                  bl = new Point(b,l),
                  br = new Point(b,r);


            dt = DistanceFromLine(p, tl, tr);
            db = DistanceFromLine(p, bl, br);
            dl = DistanceFromLine(p, tl, bl);
            dr = DistanceFromLine(p, tr, br); 

            side = LineHelper.Side.Top; 
            d = dt;
            if (db < d) { d = db; side = LineHelper.Side.Bottom; }
            if (dl < d) { d = dl; side = LineHelper.Side.Left; } 
            if (dr < d) { d = dr; side = LineHelper.Side.Right; }

            double x = p.X, y = p.Y;

            switch (side)
            {
                case LineHelper.Side.Top:
                    y = t;
                    if (x < l) x = l;
                    if (x > r) x = r;
                    break;
                case LineHelper.Side.Bottom:
                    y = b;
                    if (x < l) x = l;
                    if (x > r) x = r;
                    break;
                case LineHelper.Side.Left:
                    x = l;
                    if (y < t) y = t;
                    if (y > b) y = b;
                    break;
                case LineHelper.Side.Right:
                    x = r;
                    if (y < t) y = t;
                    if (y > b) y = b;
                    break;
            }*/

            return new Point(x, y);
        }

        public override void Resized()
        {
            base.Resized();
            foreach (UmlRelation relation in Relations)
            {
                relation.RecalcRelationLink(this);
           /*     Point p;
                if (relation.End1.EndControl == this)
                    p = relation.LineFigure.StartPoint;
                else
                {
                    int n = (relation.LineFigure.Segments[0] as PolyLineSegment).Points.Count - 1;
                    p = (relation.LineFigure.Segments[0] as PolyLineSegment).Points[n];
                }
                Point p2 = this.MagnetPoint(p);
                Drag(p2.X - p.X, p2.Y - p.Y);*/
            }
        }

        #region IDraggable Members

        public void Drag(double deltaX, double deltaY)
        {
            Point p, p2,p3;
            foreach (UmlRelation relation in Relations)
            {
                relation.RecalcRelationLink(this);
            /*    if (relation.End1.EndControl == this)
                {
                    
                    p = relation.LineFigure.StartPoint;
                    p.Offset(deltaX, deltaY);
                    relation.LineFigure.StartPoint = p;
                    p = (relation.LineFigure.Segments[0] as PolyLineSegment).Points[0];
                    p2 = (relation.LineFigure.Segments[0] as PolyLineSegment).Points[1];
                    p3 = (relation.LineFigure.Segments[0] as PolyLineSegment).Points[2];
                    if (LineHelper.GetDirection(relation.End1.Side) == LineHelper.LineDirection.Horizontal)
                    {
                        p.Offset(0, deltaY);
                        if (p2.X == p3.X)
                            p2 = new Point(p2.X, (p.Y + p3.Y) / 2);
                    }
                    else
                    {
                        p.Offset(deltaX, 0);
                        if (p2.Y == p3.Y)
                            p2 = new Point((p.X + p3.X) / 2, p2.Y);                       
                    }
                    (relation.LineFigure.Segments[0] as PolyLineSegment).Points[0] = p;
                    (relation.LineFigure.Segments[0] as PolyLineSegment).Points[1] = p2;
                }
                else
                {
                    int n = (relation.LineFigure.Segments[0] as PolyLineSegment).Points.Count - 1;
                    p = (relation.LineFigure.Segments[0] as PolyLineSegment).Points[n];
                    p.Offset(deltaX, deltaY);
                    (relation.LineFigure.Segments[0] as PolyLineSegment).Points[n] = p;
                    p = (relation.LineFigure.Segments[0] as PolyLineSegment).Points[n - 1];
                    p2 = (relation.LineFigure.Segments[0] as PolyLineSegment).Points[n - 2];
                    p3 = (relation.LineFigure.Segments[0] as PolyLineSegment).Points[n - 3];
                    if (LineHelper.GetDirection(relation.End2.Side) == LineHelper.LineDirection.Horizontal)
                    {
                        p.Offset(0, deltaY);
                        if (p2.X == p3.X)
                            p2 = new Point(p2.X, (p.Y + p3.Y) / 2);
                    }
                    else
                    {
                        p.Offset(deltaX, 0);
                        if (p2.Y == p3.Y)
                            p2 = new Point((p.X + p3.X) / 2, p2.Y);
                    }
                    (relation.LineFigure.Segments[0] as PolyLineSegment).Points[n - 1] = p;
                    (relation.LineFigure.Segments[0] as PolyLineSegment).Points[n - 2] = p2;
                }*/
            }
        }

        #endregion
    }
}
